import geopandas as gpd
import os
import matplotlib as plt

# List of base names
base_names = ["Fan District Association","Civic_Associations", "Addresses_in_fan", "Parcels_in_fan"]

# Folder where GeoJSON files are located
geojson_folder = "../precious/"
features = ["Addresses","Parcels"]
selector = "Civic_Associations"

selector_key = "Fan District Association"
##selector_key = "West Avenue Improvement Association"

input_folder = "../../data"

# Load files into a dictionary
data = {}

for name in base_names:
    file_path = os.path.join(input_folder, f"{name}.geojson")
    if os.path.exists(file_path):
        data[name] = gpd.read_file(file_path)
    else:
        print(f"current dir: {os.getcwd()}")
        print(f"⚠️ File not found: {file_path}")

data[selector_key] = data[selector][ data[selector]["Name"]==selector_key]
Skipping field AddressList: unsupported OGR type: 5
Skipping field AddressIdList: unsupported OGR type: 5
import folium
from shapely.geometry import mapping
import geopandas as gpd

# Project and compute bounding box in WGS84
fan_shape = data[selector_key].to_crs(epsg=4326)  # Folium uses WGS84

# Compute bounds: [[south, west], [north, east]]
minx, miny, maxx, maxy = fan_shape.total_bounds
bounds = [[miny, minx], [maxy, maxx]]

# Center for initial rendering (optional fallback)
center = [(miny + maxy) / 2, (minx + maxx) / 2]

# Create map and set bounds
m = folium.Map(location=center, zoom_start=15, tiles="cartodbpositron")
m.fit_bounds(bounds)
# Add neighborhood boundary
x = folium.GeoJson(
    data[selector_key].geometry,
    name="The Fan Boundary",
    style_function=lambda x: {
        "color": "black",
        "weight": 3,
        "fillOpacity": 0,
    }
).add_to(m)
# Add parcels (lighter gray polygons)
x = folium.GeoJson(
    data["Parcels_in_fan"].geometry,
    name="Parcels",
    style_function=lambda x: {
        "color": "#999999",
        "weight": 0.5,
        "fillOpacity": 0.4,
    },
).add_to(m)

# Add addresses (red points)
from shapely.geometry import Point

def get_color_for_unit_count(unit_count: int, max_unit_count: int = 10) -> str:
    if unit_count == 1:
        return "#FF0000"  # red
    
    # For unit_count > 1: magenta to white gradient
    scale = min(unit_count, max_unit_count) / max_unit_count
    green_value = int(255 * scale)  # from 0 (magenta) to 255 (white)
    
    return f"#FF{green_value:02X}FF"

for _, row in data["Addresses_in_fan"].iterrows():
    pt = row.geometry

    # Skip labels for addresses where UnitType is not None.
    unit_type = row["UnitType"]
    if unit_type is not None:
        continue

    # Only plot if geometry is a valid Point
    if not isinstance(pt, Point):
        print(f"Skipping non-Point geometry at index {_}: {type(pt)}")
        continue

    if (row.get("BuildingNumber")=="1465") and (row.get("StreetName")=="Floyd"):
        #logger.debug( row )
        pass


    unit_count = row.get("UnitCount",1)
    label = row.get("AddressLabel", "")
    if unit_count>1:
        label = label + f" ({unit_count})"
    tooltip = folium.Tooltip(label) if label.strip() else None
    color = get_color_for_unit_count(unit_count)

    folium.CircleMarker(
        location=[pt.y, pt.x],
        radius=2,
        color=color,
        fill=True,
        fill_opacity=0.8,
        tooltip=tooltip,  # Explicit safe wrapper
    ).add_to(m)

# Add layer control and display map
x = folium.LayerControl().add_to(m)
Make this Notebook Trusted to load map: File -> Trust Notebook